import os
import time
from threading import Thread
import requests
from loguru import logger
from lxml import etree
from tqdm import tqdm


class M163:
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'Connection': 'keep-alive',
        'Cookie': '_iuqxldmzr_=32; _ntes_nnid=b6a792c4643c9a73d1ed5fb230187934,1587280944045; _ntes_nuid=b6a792c4643c9a73d1ed5fb230187934; WM_TID=WHdKov4qVnpFRUEAQUZ%2BU8ITIwIM0nhx; WM_NI=%2FxCGp1yn%2FTpGQB%2BRBZJyEwfho4m2bo02YUxmXZS69kz9JjO1piFusZcAUBt1pPoOXLwVrxz%2BEumyva52h9s0lE76f5Ls19EEqMKhPZrrtrZJUwXOJvvAMHh8dX8EQztVRWg%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6ee82e27bbbb9ab89c850b8ac8aa7d55b928a9fbbb544819e9c97c9498c88bd96d42af0fea7c3b92aeda88da8f33bbbb2e1b5f34d939ee5b3c1528aa78b98e564adeca3d3e165f4ee97b6fb679c869dd7bc61bae7868bce3bb4b78a96f252a19f9ba3f73bb490a1b0db48b58abe99f92187aba39af56bf7eea8daf23cb09bf9d7e53f90adffdac84fb3938589d352b2aebc93d94297a998b0c97eb69f83a9eb34a888f8a6d472b88a9eb8e637e2a3; playerid=23047395; JSESSIONID-WYYY=E8MJ5Pu2dkQpqGAzpMyXVwP4PYak64AtG6eOl7iJCzkIwSm9cEv0pyG6yayqtjwv6PrZHct1Gs54qxaVqPWVCl5T0PUp8Tt4ZuFuM2WAZoGTQl3yigYrHlk9qSaXFfPy2UWXXTVNHybzaFJ2QpOpwKVqaB49nuHbpo7KCy4iw49d35p7%3A1590274336718',
        'DNT': '1',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
        'Referer': 'https://music.163.com/',
        'Host': 'music.163.com'
    }

    def download(self, url):
        url = url.replace('/#', '').replace('https', 'http')  # 对字符串进行去空格和转协议处理
        # 当没有输入url就点击下载或者回车的时候，在文本框中显示提示
        if url == '':
            return
        # 网易云音乐外链url接口：http://music.163.com/song/media/outer/url?id=xxxx
        out_link = 'http://music.163.com/song/media/outer/url?id='

        session = requests.Session()
        html = session.get(url=url, headers=self.headers).text

        tree = etree.HTML(html)

        # 音乐列表
        song_list = tree.xpath('//ul[@class="f-hide"]/li/a')
        # 如果是歌手页面
        artist_name_tree = tree.xpath('//h2[@id="artist-name"]/text()')
        artist_name = str(artist_name_tree[0]) if artist_name_tree else None

        # 如果是歌单页面：
        song_list_name_tree = tree.xpath('//h2[contains(@class,"f-ff2")]/text()')
        song_list_name = str(song_list_name_tree[0]) if song_list_name_tree else None
        # 设置音乐下载的文件夹为歌手名字或歌单名
        folder = artist_name if artist_name else song_list_name
        if not os.path.exists(folder):
            os.makedirs(folder)
        st_time = int(time.time())
        for i, song in enumerate(song_list, 1):
            href = str(song.xpath('./@href')[0])
            id = href.split('=')[-1]
            song_url = out_link + id  # 拼接获取音乐真实的src资源值
            title = str(song.xpath('./text()')[0])  # 音乐的名字
            filename = title + '.mp3'
            filepath = os.path.join(folder, filename)
            logger.info(f'开始下载第{i}首音乐:{filename}')
            self.download_song(song_url, filepath, session=session)
        end_time = int(time.time())
        time_take = end_time - st_time
        logger.success(f'专辑《{song_list_name}》已下载完毕,耗时{time_take}s')

    def download_song(self, song_url, filepath, session):
        location = self.get_real_address(song_url, session)
        r = session.get(location, stream=True)
        mp3_size = int(r.headers['Content-Length'])
        try:  # 下载音乐
            # 调用iter_content，一块一块的遍历要下载的内容，搭配stream=True，此时才开始真正的下载
            # iterable：可迭代的进度条 total：总的迭代次数 desc：进度条的前缀
            with open(filepath, 'wb') as f:
                for data in tqdm(
                        iterable=r.iter_content(),
                        total=mp3_size,
                        unit='b',
                        desc=filepath.split('/')[-1],
                        unit_scale=True
                ):
                    f.write(data)
        except Exception as e:
            logger.error(e)

    def get_real_address(self, url, session):
        res = session.get(url, headers=self.headers, allow_redirects=False)
        return res.headers['Location'] if res.status_code == 302 else None


if __name__ == "__main__":
    app = M163()
    url = 'https://music.163.com/#/playlist?id=4956037794'
    app.download(url)
